/*******************************************************************************
 * Copyright 2011 Mauro Luigi Drago (drago.luigi@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
/*******************************************************************************
 * Copyright (c) 2008 itemis AG (http://www.itemis.eu) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 *******************************************************************************/
package com.bytenose.extrautils.patterns.dispatcher;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * A comparator between methods.
 * This comparator has been taken and modified from XText 
 * (Copyritght 2008 itemis AG, http://www.itemis.eu).
 * This comparator orders method:
 * <ul>
 * 	<li>according to the number of parameters. Methods will less
 * 		parameters come first. If methods have the same number of
 * 		parameters, the next predicate is evaluated.
 *  <li>according to parameter types. Methods with with more
 *  	general parameter types come first. If methods arguments are
 *  	exactly of the same type, the next predicate is evaluated.
 *  <li>according to declaring class. More specific comes first.
 *  	If the methods are declared in the same class, the two methods
 *  	are equal.
 * </ul>
 * @author Mauro Luigi Drago
 *
 */
public class MethodComparator implements Comparator<Method> {
	@Override
	public int compare(Method m1, Method m2) {
		final List<Class<?>> paramTypes1 = Arrays.asList(m1.getParameterTypes());
		final List<Class<?>> paramTypes2 = Arrays.asList(m2.getParameterTypes());

		// sort by number of parameters
		if (paramTypes1.size() > paramTypes2.size())
			return 1;
		if (paramTypes2.size() > paramTypes1.size())
			return -1;

		// sort by parameter types from left to right
		for (int i = 0; i < paramTypes1.size(); i++) {
			final Class<?> class1 = paramTypes1.get(i);
			final Class<?> class2 = paramTypes2.get(i);

			if (class1.equals(class2))
				continue;
			if (class1.isAssignableFrom(class2))
				return -1;
			if (class2.isAssignableFrom(class1))
				return 1;
		}

		// sort by declaring class (more specific comes first).
		if (!m1.getDeclaringClass().equals(m2.getDeclaringClass())) {
			if (m1.getDeclaringClass().isAssignableFrom(m2.getDeclaringClass()))
				return 1;
			if (m2.getDeclaringClass().isAssignableFrom(m1.getDeclaringClass()))
				return -1;
		}

		return 0;
	}

}
